/*
    cpu.c
    Copyright (C) 2011  Jiabo <jiabo2011@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "cpu.h"

static void
cpu_dump_seg_cache(CPUX86State *env, FILE *f,
	int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
	const char *name, struct SegmentCache *sc)
{
	cpu_fprintf(f, "%-3s=%04x %08x %08x %08x", name, sc->selector,
		(uint32_t)sc->base, sc->limit, sc->flags);

	if (!(env->cr[0] & CR0_PE_MASK) || !(sc->flags & DESC_P_MASK))
		goto done;

	cpu_fprintf(f, " DPL=%d ", (sc->flags & DESC_DPL_MASK) >> DESC_DPL_SHIFT);
	if (sc->flags & DESC_S_MASK) {
		/* code or data */
		if (sc->flags & DESC_CS_MASK) {
			cpu_fprintf(f, ((sc->flags & DESC_B_MASK) ? "CS32" : "CS16"));
			cpu_fprintf(f, " [%c%c", (sc->flags & DESC_C_MASK) ? 'C' : '-',
				(sc->flags & DESC_R_MASK) ? 'R' : '-');
		} else {
			cpu_fprintf(f, (sc->flags & DESC_B_MASK) ? "DS  " : "DS16");
			cpu_fprintf(f, " [%c%c", (sc->flags & DESC_E_MASK) ? 'E' : '-',
					(sc->flags & DESC_W_MASK) ? 'W' : '-');
		}
		cpu_fprintf(f, "%c]", (sc->flags & DESC_A_MASK) ? 'A' : '-');
	} else {
		/* system */
		static const char *sys_type_name[16] = {
			"Reserved", "TSS16-avl", "LDT", "TSS16-busy",
			"CallGate16", "TaskGate", "IntGate16", "TrapGate16",
			"Reserved", "TSS32-avl", "Reserved", "TSS32-busy",
			"CallGate32", "Reserved", "IntGate32", "TrapGate32"
		};
		cpu_fprintf(f, "%s",
			sys_type_name[(sc->flags & DESC_TYPE_MASK)
				>> DESC_TYPE_SHIFT]);
	}

done:
	cpu_fprintf(f, "\n");
}

void cpu_dump_state(CPUX86State *env, FILE *f,
	int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
{
	int eflags, i;
//	char cc_op_name[32];
	static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" };

	eflags = env->eflags;

	if (env->df)
		eflags |= DF_MASK;

	cpu_fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
		"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
//		"EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d SMM=%d HLT=%d\n",
		"EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d\n",
		(uint32_t)env->regs[R_EAX],
		(uint32_t)env->regs[R_EBX],
		(uint32_t)env->regs[R_ECX],
		(uint32_t)env->regs[R_EDX],
		(uint32_t)env->regs[R_ESI],
		(uint32_t)env->regs[R_EDI],
		(uint32_t)env->regs[R_EBP],
		(uint32_t)env->regs[R_ESP],
		(uint32_t)env->eip, eflags,
		eflags & DF_MASK ? 'D' : '-',
		eflags & CC_O ? 'O' : '-',
		eflags & CC_S ? 'S' : '-',
		eflags & CC_Z ? 'Z' : '-',
		eflags & CC_A ? 'A' : '-',
		eflags & CC_P ? 'P' : '-',
		eflags & CC_C ? 'C' : '-',
		env->hflags & HF_CPL_MASK/*,
		(env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1,
		(env->a20_mask >> 20) & 1,
		(env->hflags >> HF_SMM_SHIFT) & 1,
		env->halted*/);

	for (i = 0; i < 6; i++) {
		cpu_dump_seg_cache(env, f, cpu_fprintf, seg_name[i],
			&env->segs[i]);
	}
	cpu_dump_seg_cache(env, f, cpu_fprintf, "LDT", &env->ldt);
	cpu_fprintf(f, "GDT=     %08x %08x\n",
		(uint32_t)env->gdt.base, env->gdt.limit);
	cpu_fprintf(f, "IDT=     %08x %08x\n",
		(uint32_t)env->idt.base, env->idt.limit);
	cpu_fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n",
		(uint32_t)env->cr[0],
		(uint32_t)env->cr[2],
		(uint32_t)env->cr[3],
		(uint32_t)env->cr[4]);
//	cpu_fprintf(f, "TMP_C=%d TMP_P=%d TMP_A=%d TMP_Z=%d TMP_S=%d TMP_O=%d \n",
//		(uint32_t)env->tflags[0],
//		(uint32_t)env->tflags[1],
//		(uint32_t)env->tflags[2],
//		(uint32_t)env->tflags[3],
//		(uint32_t)env->tflags[4],
//		(uint32_t)env->tflags[5]);
}

void cpu_reset(CPUX86State *env)
{
	memset(env, 0, sizeof(CPUX86State));

	env->eip = 0xfff0;

	cpu_load_seg_cache(env, R_CS, 0xf000, 0, 0xffff, 0);
	cpu_load_seg_cache(env, R_DS, 0, 0, 0xffff, 0);
	cpu_load_seg_cache(env, R_ES, 0, 0, 0xffff, 0);
	cpu_load_seg_cache(env, R_SS, 0, 0, 0xffff, 0);
	cpu_load_seg_cache(env, R_FS, 0, 0, 0xffff, 0);
	cpu_load_seg_cache(env, R_GS, 0, 0, 0xffff, 0);

	env->cr[0] = 0x60000010;
}

CPUX86State *cpu_init(void)
{
	CPUX86State *env;

	env = malloc(sizeof(CPUX86State));
	if (!env)
		return NULL;

	cpu_reset(env);

	env->eflags = 0x2;

	return env;
}

void cpu_exit(CPUX86State *env)
{
	free(env);
}
